• 问题

    延迟初始化(lazy initialization)是延迟到需要域的值时才将它初始化的这种行为。如果永远不需要这个值,这个域就永远不会被初始化。这种方法既适用于静态域,也适用于实例域。和大多数优化一样,不成熟的优化是大部分错误的源头。那么针对线程安全的延迟初始化有哪些可靠的方式?

  • 答案

    下面是正常初始化实例域的方式,但是要注意采用了final修饰符

    private final FildType field= computeFieldValue();
    

    现在要对这个实例域进行延迟初始化,有这样几种方式:

    1. 同步方法:在实例化域值得时候,可以使用同步方法从而保证线程安全性,如:

      private FieldType field;
      synchronized FieldType getField(){
          if(field == null){
              field = computeFieldValues();
          }
          return field;
      }
      
    2. 静态内部类:为了减小上面这种方式的同步访问成本,可以采用静态内部类的方式,被称之为lazy initialization holder class 模式。在jvm的优化下,这种方式不仅可以达到延迟初始化的效果,也能保证线程安全。示例代码为:

      private static class FieldHolder{
          static final FieldType field = computeFieldValue();
      }
      static FieldType getField(){
          return FieldType.field;
      }
      
    3. 双重检测:这种模式避免了在初始化之后,再次访问这个域时的锁定开销(在普通的方法里面,会使用synchronized对方法进行同步,每次访问方法的时候都要进行锁定)。这种模式的思想是:两次检查域的值,第一次检查时不锁定,看看其是否初始化;第二次检查时锁定。只用当第二次检查时,表明其没有被初始化,才会调用computeFieldValue方法对其进行初始化。如果已经被初始化了,就不会锁定了,另外该域被声明为volatile非常重要,示例代码为:

      private volatile FieldType field;
      public FieldType getField() {
          FieldType result = field;
          if (result == null) {
              synchronized (this) {
                  result = field;
                  if (result == null) {
                      field = result = computeFieldValue();
                  }
              }
          }
          return result;
      }
      
  • 结论

    大多数正常的初始化都要优于延迟初始化。如果非要进行延迟初始化的话,针对实例域采用双重检测方式,针对静态域,可以利用静态内部类的第一次访问才进行初始化的特性,使用静态内部类来完成延迟初始化

results matching ""

    No results matching ""